﻿using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.Data;
using Volo.Payment.Gateways;

namespace Volo.Payment.Requests
{
    public class PaymentRequestAppService : PaymentAppServiceBase, IRemoteService, IApplicationService, IPaymentRequestAppService
	{
		protected IPaymentRequestRepository PaymentRequestRepository { get; }

		private readonly IServiceProvider _serviceProvider;

		private readonly PaymentOptions _optionsValue;

		private readonly IOptions<PaymentOptions> _paymentOptions;

		public PaymentRequestAppService(IPaymentRequestRepository paymentRequestRepository, IServiceProvider serviceProvider, IOptions<PaymentOptions> paymentGatewayOptions, IOptions<PaymentOptions> paymentOptions)
		{
			this.PaymentRequestRepository = paymentRequestRepository;
			this._serviceProvider = serviceProvider;
			this._paymentOptions = paymentOptions;
			this._optionsValue = paymentGatewayOptions.Value;
		}

		public async Task<PaymentRequestWithDetailsDto> CreateAsync(PaymentRequestCreateDto input)
		{
			var paymentRequest = new PaymentRequest(base.GuidGenerator.Create());
			foreach (var keyValuePair in input.ExtraProperties)
			{
				paymentRequest.SetProperty<PaymentRequest>(keyValuePair.Key, keyValuePair.Value);
			}
			foreach (var paymentRequestProductCreateDto in input.Products)
			{
				paymentRequest.AddProduct(paymentRequestProductCreateDto.Code, paymentRequestProductCreateDto.Name, paymentRequestProductCreateDto.UnitPrice, paymentRequestProductCreateDto.Count, paymentRequestProductCreateDto.TotalPrice, paymentRequestProductCreateDto.ExtraProperties);
			}
			var result = await this.PaymentRequestRepository.InsertAsync(paymentRequest);
			return base.ObjectMapper.Map<PaymentRequest, PaymentRequestWithDetailsDto>(result);
		}

		public async Task<PaymentRequestWithDetailsDto> GetAsync(Guid id)
		{
			var request = await this.PaymentRequestRepository.GetAsync(id);
			return await this.GetPaymentRequestWithDetailsDto(request);
		}

		public async Task<PaymentRequestWithDetailsDto> CompleteAsync(CompletePaymentRequestDto input)
		{
			var paymentRequest = await this.PaymentRequestRepository.GetAsync(input.Id);
			using (var serviceScope = this._serviceProvider.CreateScope())
			{
				var paymentGateway = serviceScope.ServiceProvider.GetService(this._optionsValue.Gateways.Single(x => x.Key == input.GateWay).Value.PaymentGatewayType) as IPaymentGateway;
				if (paymentGateway == null)
				{
					throw new Exception("Invalid payment gateway type.");
				}
				if (!paymentGateway.IsValid(paymentRequest, input.Properties))
				{
					throw new Exception("Your payment is not valid.");
				}
			}
			foreach (var keyValuePair in input.Properties)
			{
				HasExtraPropertiesExtensions.SetProperty<PaymentRequest>(paymentRequest, keyValuePair.Key, keyValuePair.Value, true);
			}
			paymentRequest.Gateway = input.GateWay;
			paymentRequest.Complete(true);
			await this.PaymentRequestRepository.UpdateAsync(paymentRequest);
			return await this.GetPaymentRequestWithDetailsDto(paymentRequest);
		}

		public async Task<PaymentRequestWithDetailsDto> SetCurrencyAsync(SetPaymentRequestCurrencyDto input)
		{
			var paymentRequest = await this.PaymentRequestRepository.GetAsync(input.Id);
			paymentRequest.Currency = input.Currency;
			return await this.GetPaymentRequestWithDetailsDto(paymentRequest);
		}

		public Task<PaymentGatewayConfigurationDictionary> GetGatewayConfigurationAsync()
		{
			return Task.FromResult<PaymentGatewayConfigurationDictionary>(this._paymentOptions.Value.Gateways);
		}

		private async Task<PaymentRequestWithDetailsDto> GetPaymentRequestWithDetailsDto(PaymentRequest request)
		{
			var paymentRequest = await this.PaymentRequestRepository.GetAsync(request.Id);
			return base.ObjectMapper.Map<PaymentRequest, PaymentRequestWithDetailsDto>(paymentRequest);
		}
	}
}
